﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Freedom.Common.Collections
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public abstract class ObjectCollectionBase<T> : IEnumerable<T>, IObjectCollectionBase, IDisposable
        where T : class
    {
        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ObjectCollectionBase&lt;T&gt;"/> is initilized.
        /// </summary>
        /// <value><c>true</c> if initilized; otherwise, <c>false</c>.</value>
        public bool Initilized { get; private set; }
        /// <summary>
        /// Items
        /// </summary>
        protected readonly List<T> Items = new List<T>();
        /// <summary>
        /// SynchRoot
        /// </summary>
        protected object SynchRoot { get; private set; }


        /// <summary>
        /// Gets the count.
        /// </summary>
        /// <value>The count.</value>
        public int Count
        {
            get { return Items.Count; }
        }
        #endregion

        /// <summary>
        /// Occurs when [collection reload].
        /// </summary>
        [field: NonSerialized]
        public event EventHandler CollectionReload;


        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectCollectionBase&lt;T&gt;"/> class.
        /// </summary>
        protected ObjectCollectionBase()
        {
            SynchRoot= new object();
        }


        #region Interface

        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public void Refresh()
        {
            LoadItems(false);
        }

        /// <summary>
        /// Toes the array.
        /// </summary>
        /// <returns></returns>
        public T[] ToArray()
        {
            lock (SynchRoot)
            {
                return Items.ToArray();
            }
        }
        /// <summary>
        /// Toes the array.
        /// </summary>
        /// <returns></returns>
        object[] IObjectCollectionBase.ToArray()
        {
            lock (SynchRoot)
            {
                return Items.ToArray();
            }
        }


        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public virtual IEnumerator<T> GetEnumerator()
        {
            lock (SynchRoot)//Not locking Enumerator
            {
                return new Enumerator(Items.ToArray());
            }
        }


        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion


        #region Protected
        /// <summary>
        /// Initializes this instance.
        /// </summary>
        protected void Initialize()
        {
            LoadItems(true);
            Initilized = true;
        }
        /// <summary>
        /// Loads the items.
        /// </summary>
        /// <param name="fromConstructor">if set to <c>true</c> [from constructor].</param>
        protected abstract void LoadItems(bool fromConstructor);

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                Items.Clear();
            }
        }


        /// <summary>
        /// Reloads the specified items.
        /// </summary>
        /// <param name="items">The items.</param>
        protected virtual void SetItems(IEnumerable<T> items)
        {
            lock (SynchRoot)
            {
                Items.Clear();
                Items.AddRange(items);
            }
            OnCollectionReset();
        }

        /// <summary>
        /// Clears this instance.
        /// </summary>
        protected virtual void Clear()
        {
            Items.Clear();
            OnCollectionReset();
        }


        /// <summary>
        /// Finds the specified match.
        /// </summary>
        /// <param name="match">The match.</param>
        /// <param name="index">The index.</param>
        /// <returns>Index of first founded  intem</returns>
        protected T Find(Predicate<T> match, out int index)
        {
            try
            {
                for (index = 0; index < Items.Count; index++)
                {
                    var item = Items[index];
                    if (match(item))
                    {
                        return item;
                    }
                }
                index = -1;
                return null;
            }
            catch
            {
                index = -1;
                return null;
            }
        }
        /// <summary>
        /// Finds the specified match.
        /// </summary>
        /// <param name="match">The match.</param>
        /// <returns></returns>
        protected T Find(Predicate<T> match)
        {
            return Items.Find(match);
        }
        /// <summary>
        /// Called when [collection reset].
        /// </summary>
        protected virtual void OnCollectionReset()
        {
            var tmp = CollectionReload;
            if (tmp != null) tmp(this, EventArgs.Empty);
        }



        #endregion







        /// <summary>
        /// Non versioned List enumrator
        /// </summary>
        [Serializable, StructLayout(LayoutKind.Sequential)]
        public struct Enumerator : IEnumerator<T>
        {

            private int index;
            private T[] _items;
            private T current;

            /// <summary>
            /// Initializes a new instance of the <see cref="ObjectCollectionBase{T}.Enumerator"/> struct.
            /// </summary>
            /// <param name="list">The list.</param>
            internal Enumerator(T[] list)
            {
                _items = list;
                index = 0;
                current = default(T);
            }

            #region Interface

            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            public void Dispose()
            {
                _items = null;
            }

            /// <summary>
            /// Advances the enumerator to the next element of the collection.
            /// </summary>
            /// <returns>
            /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
            /// </returns>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            public bool MoveNext()
            {

                if ((index < _items.Length))
                {
                    current = _items[index];
                    index++;
                    return true;
                }
                return MoveNextRare();
            }


            /// <summary>
            /// Gets the current.
            /// </summary>
            /// <value>The current.</value>
            public T Current
            {
                get { return current; }
            }
            /// <summary>
            /// Gets the current.
            /// </summary>
            /// <value>The current.</value>
            object IEnumerator.Current
            {
                get { return current; }
            }
            /// <summary>
            /// Sets the enumerator to its initial position, which is before the first element in the collection.
            /// </summary>
            /// <exception cref="T:System.InvalidOperationException">
            /// The collection was modified after the enumerator was created.
            /// </exception>
            void IEnumerator.Reset()
            {
                index = 0;
                current = default(T);
            }
            #endregion

            private bool MoveNextRare()
            {

                index = _items.Length + 1;
                current = default(T);
                return false;
            }
        }


    }
}
